#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <ucontext.h>
#include <unistd.h>
#include <sys/mman.h>
#include <malloc.h>
#include "threadStructure.h"
#include "thread.h"

#define handle_error(msg)				\
  do { perror(msg); exit(EXIT_FAILURE); } while (0)

static thread* running_thread = NULL;

void setRunningThread(thread* th){
  running_thread = th;
}

void handler(void *contextF){
  ucontext_t *context = (ucontext_t *)contextF;
  thread * thread = getRunningThread();
  if (thread == NULL)
    exit(1);
  printf("Thread number %d: Got SIGSEGV at address: %p\n",
	 thread->tid, context);
  printf("Thread number %d has been killed\n", thread->tid);
  thread_exit(NULL);
  exit(EXIT_FAILURE);
}

int getRunningThreadTid(){
  if(running_thread == NULL)
    return -1;
  return running_thread->tid;
}

thread* getRunningThread(){
  return running_thread;
}

int estLeThreadCourant(int tid){
  return (tid == getRunningThreadTid()); 
}

ucontext_t* createContext(void(*func)(void),void *funcarg){
  ucontext_t * uc = (ucontext_t*)malloc(sizeof(ucontext_t));
  static int pagesize = 0;
  char *buffer;
  static int hasbeencalled = 1;
  if(!hasbeencalled){
    hasbeencalled = 0;
    struct sigaction sa;
    pagesize = sysconf(_SC_PAGE_SIZE);
    if (pagesize == -1)
      handle_error("sysconf");
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sa.sa_sigaction = (void(*)(int, siginfo_t *, void *))handler;
    if (sigaction(SIGSEGV, &sa, NULL) == -1)
      handle_error("sigaction");
   }
  /* Allocate a buffer aligned on a page boundary;
     initial protection is PROT_READ | PROT_WRITE */
  buffer = (char *) memalign(pagesize, 16 * pagesize);
  if (buffer == NULL)
    handle_error("memalign");
  uc->uc_stack.ss_size = 4 * 4096;
  uc->uc_stack.ss_sp = malloc(4 * 4096);
  getcontext(uc);
  makecontext(uc, func, 1, funcarg);
  return uc;
}

thread * newThread(int tid, status state, ucontext_t* uc){
  thread * t = (thread *)malloc(sizeof(thread)); 
  if(t){
    t->tid = tid;
    t->context = uc;
    t->state = state;
    t->is_father = FALSE;
    t->est_attendu = FALSE;
    t->a_le_verou = FALSE;
#ifdef O_PRIORITE
    t->priority.priorite_max = 3; //priorite 3 par defaut
    t->priority.priorite_actu = 3;
#endif
    return t;
  }
  return NULL;
}
 
/*
 * generation du TID unique d'un thread
 */
int getNextTID(){
  static int TID = 0;
  return TID++;
}

/*
 *
 */
void * getValRetour(thread* th){
  return th->valRetour;
};

/*
 *
 */
enum status getStatus(thread* th){
  return th->state;
}
